﻿#include "StringConverter.h"
#include "XyNet/Crypto/CryptoPP.h"

#ifdef XYNET_PLATFORM_WINDOWS
#	include <boost/locale/encoding_utf.hpp>
#endif

namespace XyNet
{
	bool StringConverter::isLittleEndian()
	{
		static uint32_t _endian_x_ = 1;
		return (reinterpret_cast<const uint8_t*>(&_endian_x_))[0] != 0;
	}

	bool StringConverter::isBigEndian()
	{
		return !isLittleEndian();
	}

	int StringConverter::bytesToInt(const Bytes& input)
	{
		assert(input.size() == sizeof(int));
		int output;
		if (isLittleEndian())
		{
			output = (int)(((input[0] & 0xFF) << 24)
				| ((input[1] & 0xFF) << 16)
				| ((input[2] & 0xFF) << 8)
				| (input[3] & 0xFF));
		}
		else
		{
			output = (int)((input[0] & 0xFF)
				| ((input[1] & 0xFF) << 8)
				| ((input[2] & 0xFF) << 16)
				| ((input[3] & 0xFF) << 24));
		}
		return output;
	}

	Bytes StringConverter::intToBytes(int input)
	{
		Bytes output(sizeof(int), 0);
		if (isLittleEndian())
		{
			output[0] = (input >> 24) & 0xFF;
			output[1] = (input >> 16) & 0xFF;
			output[2] = (input >> 8) & 0xFF;
			output[3] = input & 0xFF;
		}
		else
		{
			output[3] = (input >> 24) & 0xFF;
			output[2] = (input >> 16) & 0xFF;
			output[1] = (input >> 8) & 0xFF;
			output[0] = input & 0xFF;
		}
		return output;
	}

	//////////////////////////////////////////////////////////////////////////

	string StringConverter::bytesToHexString(const Bytes& input, bool uppercase)
	{
		using namespace CryptoPP;
		string output;
		StringSource(input, true,
			new HexEncoder(new StringSink(output), uppercase));
		return output;
	}

	Bytes StringConverter::hexStringToBytes(const string& input)
	{
		using namespace CryptoPP;
		Bytes output;
		StringSource(input, true, new HexDecoder(new StringSink(output)));
		return output;
	}

	string StringConverter::bytesToBase64String(const Bytes& input)
	{
		using namespace CryptoPP;
		string output;
		StringSource(input, true,
			new Base64Encoder(new StringSink(output)));
		output.pop_back();
		return output;
	}

	Bytes StringConverter::base64StringToBytes(const string& input)
	{
		using namespace CryptoPP;
		Bytes output;
		StringSource(input, true, new Base64Decoder(new StringSink(output)));
		return output;
	}

	string StringConverter::localToUtf8(const string& input)
	{
#ifdef XYNET_PLATFORM_WINDOWS
		return wideStringToUtf8(filesystem::path(input).wstring());
#else
		return input;
#endif
	}

	string StringConverter::utf8ToLocal(const string& input)
	{
#ifdef XYNET_PLATFORM_WINDOWS
		return filesystem::path(utf8ToWideString(input)).string();
#else
		return input;
#endif
	}

#ifdef XYNET_PLATFORM_WINDOWS
	wstring StringConverter::utf8ToWideString(const string& input)
	{
		return boost::locale::conv::utf_to_utf<wchar_t>(input);
	}

	string StringConverter::wideStringToUtf8(const wstring& input)
	{
		return boost::locale::conv::utf_to_utf<char>(input);
	}
#endif
}